#ifndef __M_match_manager_H__
#define __M_match_manager_H__

#include "room.hpp"
#include "online.hpp"
#include "db.hpp"
#include "util.hpp"
#include <list>
#include <condition_variable>
#include <mutex>
#include <thread>

template <class T>
class match_queue
{
private:
    // 线程同步与互斥
    std::mutex _mutex;
    // 条件变量主要为了阻塞消费者，当队列中元素个数 < 2，则线程阻塞，>= 2的时候线程出队数据，为两个用户创建游戏房间
    std::condition_variable _cond;
    // 中间有删除指定数据的需要，所以用list
    std::list<T> _list;

public:
    match_queue() {}
    ~match_queue() {}

    void push(const T &data) // 入队一次，唤醒一次消费线程，看看此时队列元素个数是否 >= 2
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();
        // 哪个线程在这个条件变量下等待，哪个线程就会被唤醒，今天的thread和blockqueue和mutex和cond都是1:1:1的，所以notify_all 其实就是唤醒
        // 在cond条件变量下等待的一个thread
    }
    bool pop(T &data) // 出队头+获取数据
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_list.empty())
            return false;
        data = _list.front();
        _list.pop_front();
        return true;
    }
    void remove(T &data) // 移除指定数据
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }
    int size() // 获取队列元素个数
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
    bool empty() // 判断队列是否为空
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }
    void wait() // 阻塞线程
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
        // explicit unique_lock (mutex_type& m); 哦shit，库里面不让隐式类型转换，禁止从内置类型到自定义类型的单参数构造
        // 这里隐含单参数构造，_mutex构造出来unique_lock<mutex>对象，然后在传递给_cond的wait接口参数
        // 构造+拷贝构造
    }
};

class match_manager
{
private:
    // 三个不同档次的匹配队列
    match_queue<int> _q_bronze;
    match_queue<int> _q_silver;
    match_queue<int> _q_golden;
    // 分别对应每个匹配队列的三个消费线程
    std::thread _th_bronze;
    std::thread _th_silver;
    std::thread _th_golden;
    // 三个管理模块句柄
    room_manager *_rm;
    user_table *_ut;
    online_manager *_om;
    // wsserver_t *_svr;

private:
    void handle_match(match_queue<int> &mq)
    {
        while (true) // 线程进行匹配的处理，一定是死循环，一直判断blockqueue中是否至少有2个用户
        {
            // 1.判断队列人数是否大于等于2
            if (mq.size() >= 2)
            {
                // 1.取出队列的两个玩家
                int uid1, uid2;
                bool ret1 = mq.pop(uid1);
                if (ret1 == false)
                    continue;
                bool ret2 = mq.pop(uid2);
                if (ret2 == false)
                {
                    mq.push(uid1);
                    continue;
                }
                // 2.判断两个玩家是否在线(匹配过程中，某一个玩家可能会掉线，如果有人掉线，则需要把另一个人重新添加到队列里)
                wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
                if (conn1.get() == nullptr)
                {
                    mq.push(uid2);
                    continue;
                }
                wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
                if (conn2.get() == nullptr)
                {
                    mq.push(uid1);
                    continue;
                }
                // 3.为两个玩家创建房间，并将玩家加入房间中
                room_ptr rp = _rm->create_room(uid1, uid2);

                if (rp.get() == nullptr)
                {
                    mq.push(uid1);
                    mq.push(uid2);
                    continue;
                } // 房间创建失败，把两个用户重新放到队列里面
                // 4.对两个玩家进行匹配成功消息的响应
                Json::Value resp;
                resp["optype"] = "match_success";
                resp["result"] = true;
                std::string body;
                json_util::serialize(resp, body);
                conn1->send(body); // 默认以text格式发送
                conn2->send(body);
            }
            else
            {
                mq.wait();
            }
        }
    }
    void th_bronze_entry() { handle_match(_q_bronze); }
    void th_silver_entry() { handle_match(_q_silver); }
    void th_golden_entry() { handle_match(_q_golden); }

public:
    match_manager(room_manager *rm, user_table *ut, online_manager *om)
        : _rm(rm), _ut(ut), _om(om), _th_bronze(&match_manager::th_bronze_entry, this),
          _th_silver(&match_manager::th_silver_entry, this), _th_golden(&match_manager::th_golden_entry, this)
    {
        DLOG("匹配队列管理模块初始化完毕");
    }
    ~match_manager() {}
    bool add(int uid) // 添加用户到指定匹配队列里面
    {
        // 1.判断uid对应的天梯分数是哪个档次
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if (ret == false)
        {
            DLOG("获取用户%d信息失败", uid);
            return false;
        }
        int score = user["score"].asInt();
        // 2.添加到指定队列中
        if (score < 2000)
        {
            _q_bronze.push(uid);
        }
        else if (score >= 2000 && score < 3000)
        {
            _q_silver.push(uid);
        }
        else
        {
            _q_golden.push(uid);
        }
        return true;
    }
    bool del(int uid)
    {
        // 1.判断uid对应的天梯分数是哪个档次，从而判断用户在哪个档次的队列中
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if (ret == false)
        {
            DLOG("获取用户%d信息失败", uid);
            return false;
        }
        int score = user["score"].asInt();
        // 2.从指定队列中移除用户
        if (score < 2000)
        {
            _q_bronze.remove(uid);
        }
        else if (score >= 2000 && score < 3000)
        {
            _q_silver.remove(uid);
        }
        else
        {
            _q_golden.remove(uid);
        }
        return true;
    }
};

#endif